__author__ = 'YinDu'

import redis
import datetime


def try_data_redis_exception(func):
    """
        Redis read data processing
    """

    def inner(*args, **kwargs):
        try:
            redis_data = func(*args, **kwargs)
            if redis_data is not None:
                redis_data = eval(redis_data.decode())
            return redis_data
        except:
            return False

    return inner


def try_operation_redis_exception(func):
    """
        Redis operation processing
    """

    def inner(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except:
            return False

    return inner


def try_read_time_redis_exception(func):
    """
        Redis operation processing
    """

    def inner(*args, **kwargs):
        try:
            redis_data = func(*args, **kwargs)
            if redis_data == -2 or redis_data == -1:
                return False
            return redis_data
        except:
            return False

    return inner


class LinkRedis(object):
    """
        Link to redis database to operate on redis database
    """

    def __init__(self, lhost='127.0.0.1', lport=6379, ldb=1):
        self.some_redis = redis.Redis(host=lhost, port=lport, db=ldb)

    @try_operation_redis_exception
    def set_redis(self, sk, sv, st=None):
        """
            setex_time: Set the storage time of the field (timestamp)
            return: True
        """
        if st:
            self.some_redis.setex(sk, st, sv)
        else:
            self.some_redis.set(sk, sv)
        return True

    @try_operation_redis_exception
    def mset_redis(self, md):
        """
            Batch Add (dictionary)
            return: True
        """
        self.some_redis.mset(md)
        return True

    @try_operation_redis_exception
    def incr_redis(self, idata):
        """
            Automatically add
            return: True
        """
        self.some_redis.incr(idata)
        return True

    @try_operation_redis_exception
    def del_redis(self, *keys):
        """
            Delete the data
            return: True
        """
        self.some_redis.delete(*keys)
        return True

    @try_operation_redis_exception
    def hset_redis(self, hkey, hvalue, hscore):
        """
            Add hash data
            return: True
        """
        self.some_redis.hset(hkey, hvalue, hscore)
        return True

    @try_operation_redis_exception
    def zadd_redis(self, hkey, *keys):
        """
            Add collection data
            return: True
        """
        self.some_redis.zadd(hkey, *keys)
        return True

    @try_operation_redis_exception
    def hincrby_redis(self, hkey, hvalue, hscore, zincrby=False):
        """
            Add hash data
            :param zincrby: (True) an ordered set
                            (False) unordered collection
            return: True
        """
        if zincrby:
            self.some_redis.zincrby(hkey, hvalue, hscore)
        else:
            self.some_redis.hincrby(hkey, hvalue, hscore)
        return True

    @try_data_redis_exception
    def get_redis(self, gk):
        rd = self.some_redis.get(gk)
        return rd

    @try_read_time_redis_exception
    def get_time_redis(self, gk):
        """
            return: Time remaining in the cache
        """
        return self.some_redis.ttl(gk)

    @try_operation_redis_exception
    def hget_redis(self, hk, hv=None):
        """
            hget_value:
            (None) All the data
            return: The list of data
            (Not None) Specify the data
            return: The str of data
        """
        if hv:
            rd = [{data_key.decode(): data_value.decode()} for data_key, data_value in
                  self.some_redis.hgetall(hk).items()]
        else:
            rd = self.some_redis.hget(hk, hv)
        return rd

    @try_operation_redis_exception
    def zrange_redis(self, hkey, sn=0, en=-1, wb=True, rev=False):
        """
            star_num: Start the data
            end_num: End of the data
            rev: (True) Reverse order
                 (False) Positive sequence
            withscores_bool: indicates to return the scores along with the values
            return: The list of data
        """
        if rev:
            rd = self.some_redis.zrevrange(hkey, sn, en, withscores=wb)
        else:
            rd = self.some_redis.zrange(hkey, sn, en, withscores=wb)
        if wb:
            rd = [{the_redis_data[0].decode(): the_redis_data[1]} for the_redis_data in rd]
        else:
            rd = [the_redis_data.decode() for the_redis_data in rd]
        return rd

    @try_operation_redis_exception
    def set_time_redis(self, value, year=1, month=1, day=1, hk=0, mk=0, sk=0, ts=None):
        """
            ts: timestamp
            If false is returned, there is no such data
        """
        if ts:
            ts = int(ts)
        else:
            ts = datetime.datetime(year, month, day, hk, mk, sk)
        return self.some_redis.expireat(value, ts)


lredis = LinkRedis()
